
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

// { <VERSION>

//@version=5

// } <VERSION>



// { <DECLARATION STATEMENT>

indicator(title = "VitAlgo Chart Reader", shorttitle = "Vit Chart Reader", overlay = true, max_boxes_count = 500, max_lines_count = 500)

// } <DECLARATION STATEMENT>



// { <CONSTANTS>

OB_BORDER_WIDTH = 2

// } <CONSTANTS>



// { <INPUTS>

tuning   = input.int(
 title   = "Tuning",
  defval = 8,
   group = "Main Settings")

amountOfBoxes = input.int(
 title        = "Amount Of Grids",
  defval      = 3,
   group      = "Main Settings")

mitigationMethod = input.string(
 title           = "Mitigation Method",
  defval         = "Close Engulfs 100% Of Order Block",
   group         = "Main Settings",
     inline      = "mitigation",
      options    = [
       "Close Engulfs 100% Of Order Block",
         "Close Engulfs 75% Of Order Block",
          "Close Engulfs 50% Of Order Block",
           "Close Engulfs 25% Of Order Block",
             "Wick Engulfs 100% Of Order Block",
              "Wick Engulfs 75% Of Order Block",
               "Wick Engulfs 50% Of Order Block",
                 "Wick Engulfs 25% Of Order Block"])

obHighVolumeColor = input.color(
 title            = "",
  defval          = color.new(#2962ff, 30),
   group          = "Color Settings", inline = "colors")

obLowVolumeColor = input.color(
 title           = "",
  defval         = color.new(#ff9800, 65),
   group         = "Color Settings",
     inline      = "colors")

obBorderColor = input.color(
 title        = "",
  defval      = color.new(#e65100, 85),
   group      = "Color Settings",
     inline   = "colors")
   
obLinefillColor = input.color(
 title          = "",
  defval        = color.new(#787b86, 75),
   group        = "Color Settings",
     inline     = "colors")

// } <INPUTS>



// { <FUNCTIONS>

checkObCondition(set)=>
    bear = false
    for i = tuning - 1 to 0
        start = tuning - 1
        if i == start
            if close[i] <= open[i]
                break
        else
            if close[i] > open[i]
                break
            
            if i == 0
                bear := true

    bull = false
    for i = tuning - 1 to 0
        start = tuning - 1
        if i == start
            if close[i] >= open[i]
                break
        else
            if close[i] < open[i]
                break
            
            if i == 0
                bull := true
    
    [bear, bull]

// } <FUNCTIONS>



// { <USER DEFINED TYPES>

type orderBlock
    line topLine
    line botLine
    linefill bgFill
    array<box> boxArray = na
    array<float> boxVolume = na
    float topValue
    float botValue
    int leftTime
    int rightTime
    string direction

method generateBorderLines(orderBlock self, topValue, botValue)=>
    newTopLine = line.new(x1 = self.leftTime, y1 = topValue, x2 = time, y2 = topValue, xloc = xloc.bar_time, extend = extend.none, color = obBorderColor, style = line.style_solid, width = OB_BORDER_WIDTH)
    newbotLine = line.new(x1 = self.leftTime, y1 = botValue, x2 = time, y2 = botValue, xloc = xloc.bar_time, extend = extend.none, color = obBorderColor, style = line.style_solid, width = OB_BORDER_WIDTH)
    newlinefill = linefill.new(newTopLine, newbotLine, obLinefillColor)
    self.topLine := newTopLine
    self.botLine := newbotLine
    self.bgFill := newlinefill

method generateVolume(orderBlock self, topValue, botValue, vArray, hArray, lArray)=>
    newVolumeArray = self.boxVolume
    startingValue = topValue
    increment = (topValue - botValue) / amountOfBoxes
    for i = 0 to amountOfBoxes - 1
        topOfGrid = startingValue - (increment * i)
        botOfGrid = startingValue - (increment * (i + 1))
        if array.size(vArray) > 0 and array.size(hArray) > 0 and array.size(lArray) > 0
            for j = 0 to array.size(vArray) - 1
                candleVolume = array.get(vArray, j)
                candleHigh = array.get(hArray, j)
                candleLow = array.get(lArray, j)
                ltfDiff = candleHigh - candleLow

                
                if candleLow <= topOfGrid and candleHigh >= botOfGrid
                    topRegister = math.min(candleHigh, topOfGrid)
                    botRegister = math.max(candleLow, botOfGrid)

                    registerDiff = topRegister - botRegister
                    registerVolume = registerDiff / ltfDiff
                    array.set(newVolumeArray, i, array.get(newVolumeArray, i) + nz(registerVolume * candleVolume))

    array.sum(newVolumeArray)

method generateBoxes(orderBlock self, topValue, botValue, leftValue, rightValue)=>
    newBoxesArray = array.new_box()

    highestVolume = array.max(self.boxVolume)
    lowestVolume = array.min(self.boxVolume)
    timeLength = self.rightTime - self.leftTime
    timeRatio = timeLength / highestVolume

    startingValue = topValue
    increment = (topValue - botValue) / amountOfBoxes
    for i = 0 to amountOfBoxes - 1
        topOfGrid = startingValue - (increment * i)
        botOfGrid = startingValue - (increment * (i + 1))
        color_ = color.from_gradient(array.get(self.boxVolume, i), lowestVolume, highestVolume, obLowVolumeColor, obHighVolumeColor)
        newbox = box.new(left = self.leftTime, top = topOfGrid, right = self.leftTime + math.round(array.get(self.boxVolume, i) * timeRatio), bottom = botOfGrid, border_color = color_, border_width = 2, xloc = xloc.bar_time, bgcolor = color_, extend = extend.none)
        array.push(newBoxesArray, newbox)
    
    self.boxArray := newBoxesArray

method updateBoxes(orderBlock self, currentTime)=>
    self.rightTime := currentTime

    highestVolume = array.max(self.boxVolume)
    lowestVolume = array.min(self.boxVolume)
    timeLength = self.rightTime - self.leftTime
    timeRatio = timeLength / highestVolume

    for i = 0 to amountOfBoxes - 1
        box.set_right(array.get(self.boxArray, i), self.leftTime + math.round(array.get(self.boxVolume, i) * timeRatio))
    
    line.set_x2(self.topLine, self.rightTime)
    line.set_x2(self.botLine, self.rightTime)

method wipeBlock(orderBlock self)=>
    line.delete(self.topLine)
    line.delete(self.botLine)
    linefill.delete(self.bgFill)
    for i = array.size(self.boxArray) - 1 to 0
        selectedBox = array.get(self.boxArray, i)
        box.delete(selectedBox)

// } <USER DEFINED TYPES>



// { <CALCULATIONS>

rawTimeframe = timeframe.isdaily ? 1440 : timeframe.isweekly ? 1440 * 7 : timeframe.ismonthly ? 1440 * 30 : str.tonumber(timeframe.period)
fixedTimeframe = math.round(rawTimeframe / 15) < 1 ? "30S" : str.tostring(math.round(rawTimeframe / 15))
[h, l, v] = request.security_lower_tf(syminfo.tickerid, fixedTimeframe, [high, low, volume])

[bear, bull] = checkObCondition(tuning)

var array<orderBlock> orderBlockArray = array.new<orderBlock>(0)

if not na(bar_index[tuning]) and barstate.isconfirmed
    topValue = high[tuning - 1]
    botValue = low[tuning - 1]
    leftValue = time[tuning - 1]
    rightValue = time
    if bull or bear
        neworderBlock = orderBlock.new(topValue = topValue, botValue = botValue, leftTime = leftValue, rightTime = rightValue, boxVolume = array.new_float(amountOfBoxes, 0), direction = bull ? "Bull" : "Bear")
        neworderBlock.generateBorderLines(neworderBlock.topValue, neworderBlock.botValue)
        vol = neworderBlock.generateVolume(neworderBlock.topValue, neworderBlock.botValue, v[tuning - 1], h[tuning - 1], l[tuning - 1])
        neworderBlock.generateBoxes(neworderBlock.topValue, neworderBlock.botValue, neworderBlock.leftTime, neworderBlock.rightTime)
        if vol == 0
            neworderBlock.wipeBlock()
        else
            array.push(orderBlockArray, neworderBlock)

maxBlocks = math.floor(500 / amountOfBoxes)
if array.size(orderBlockArray) > 0
    for i = array.size(orderBlockArray) - 1 to 0
        block = array.get(orderBlockArray, i)
        block.updateBoxes(time)

        blockDifference = block.topValue - block.botValue
        startingValue = block.direction == "Bull" ? block.topValue : block.botValue
        sourceToUse = str.contains(mitigationMethod, "Close") ? close : 
         (block.direction == "Bull" ? low : high)
        incrementMultiplier = 
         str.contains(mitigationMethod, "100%") ? math.abs(blockDifference * 1) :
          str.contains(mitigationMethod, "75%") ? math.abs(blockDifference * .75) :
           str.contains(mitigationMethod, "50%") ? math.abs(blockDifference * .50) : .25
        incrementMultiplier *= block.direction == "Bull" ? -1 : 1 
        breakValue = startingValue + incrementMultiplier

        bullBreak = block.direction == "Bull" and sourceToUse < breakValue
        bearBreak = block.direction == "Bear" and sourceToUse > breakValue
        if (bullBreak or bearBreak or i < (array.size(orderBlockArray) - 1 - maxBlocks)) and barstate.isconfirmed
            block.wipeBlock()
            array.remove(orderBlockArray, i)

// } <CALCULATIONS>

// SD zones

//-----------------------------------------------------------------------------{
//Constants
//-----------------------------------------------------------------------------{
int DEMAND_ZONE = 1
int SUPPLY_ZONE = 2
int TBD_ZONE = 0

//-----------------------------------------------------------------------------{
//Settings
//-----------------------------------------------------------------------------{
// -- Indecision ----
indecision_factor = input.int(2, 'Min. Factor', minval = 1, group = "Indecision")
volume_periods = input.int(26, 'Previous Periods', minval = 1, group = "Indecision")
volume_chg = input.int(150, 'Volume Change (%)', minval = 1, group = "Indecision")
confirm_bars = input.int(2, 'Confirmation Bars', minval = 1, group = 'Indecision')
invalidation = input.string('Wick', 'Invalidation Method', options = ['Close', 'Wick'], group = "Indecision")

// -- Zones ----
showlast = input.int(10, 'Show Last', minval = 1, group = 'Zones')
demand_zone_css = input.color(color.new(#089981, 70), 'Demand', inline = "Zones", group = 'Zones')
supply_zone_css = input.color(color.new(#5f3a97, 70), 'Supply', inline = "Zones", group = 'Zones')
indecision_zone_css = input.color(color.new(#2962ff, 70), 'Indecision', inline = "Zones",group = 'Zones')
text_size = input.string('Small', 'Text Size', options = ['Tiny', 'Small', 'Normal'], group = "Zones")

//Global variables
//-----------------------------------------------------------------------------{
var zone_boxes = array.new_box(0)
var zone_labels = array.new_label(0)
type Zone
    float top
    float bottom
    float volume
    int left
    int type = 0
    color css
    int reached = 0
    int bars_outside = 0

var zones = array.new<Zone>(0)
var last_zone_reached = Zone.new()
bool demand_zone_confirmed = false
bool supply_zone_confirmed = false
bool price_enter_supply_zone = false
bool price_enter_demand_zone = false
var label_size = text_size == 'Tiny' ? size.tiny: text_size == 'Small' ? size.small : size.normal

//------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------
get_volume(include_current_bar, bars) =>
    past_vol = array.new_float(0)
    for i = include_current_bar ? 0: 1 to bars
        array.push(past_vol, volume[i])
    array.avg(past_vol)

is_indecision_bar() =>
    //Is an indecision candle?
    float high_change = 0.
    float low_change = 0.
    float body_change = math.abs((close - open) / open)   

    if close > open
        high_change := math.abs((high - close) / close)
        low_change := math.abs((low - open) / open)
    else 
        high_change := math.abs((high - open) / open)
        low_change := math.abs((low - close) / close)

    float factor = math.floor(math.max(high_change, low_change) / body_change)
    factor := na(factor) or factor > 999999 ? 999999 : factor
    bool add_as_new_zone = false  
    if factor >= indecision_factor   
        add_as_new_zone := true     
        for zone in zones
            //if the candle its include in some previous candle don't add
            if (high < zone.top and high > zone.bottom) and (low < zone.top and low > zone.bottom)
                add_as_new_zone := false
                break
            else if zone.type == DEMAND_ZONE and (low < zone.top and low > zone.bottom) and (high >= zone.top)
                add_as_new_zone := false
                break
            else if zone.type == SUPPLY_ZONE and (high < zone.top and high > zone.bottom) and (low <= zone.bottom)
                add_as_new_zone := false
                break
    add_as_new_zone

valid_zone(zone, bclose, bhigh, blow) =>        
    if zone.type == DEMAND_ZONE 
        demand_bar_price = invalidation == 'Close' ? bclose : blow
        demand_bar_price >= zone.bottom //and bopen > zone.bottom          
    else if zone.type == SUPPLY_ZONE 
        supply_bar_price = invalidation == 'Close' ? bclose : bhigh
        supply_bar_price <= zone.top //and bopen < zone.top
    else 
        true
    
bar_reached_zone(zone, wick) =>
    (wick >= zone.bottom and wick <= zone.top) or (close >= zone.bottom and close <= zone.top) or (open >= zone.bottom and open <= zone.top)

is_lateral_movement(zone, candles) =>
    int inside = 0
    for i = 1 to candles
        if (close[i] < zone.top and close[i] > zone.bottom) and (open[i] < zone.top and open[i] > zone.bottom)
            inside += 1
    inside >= candles

confirm_zone(zone) =>
    valid_zone = true
    new_volume = get_volume(true, confirm_bars)
    zone.volume := math.round((new_volume - zone.volume) / zone.volume * 100, 0) 
    if math.max(open, close) > zone.top
        zone.type := DEMAND_ZONE
        zone.css := demand_zone_css            
    else if math.min(open, close) < zone.bottom
        zone.type := SUPPLY_ZONE
        zone.css := supply_zone_css

    if math.abs(zone.volume) >= volume_chg 
      and zone.type != TBD_ZONE 
      and not is_lateral_movement(zone, confirm_bars)                      
        for i = 0 to confirm_bars - 1
            if not valid_zone(zone, close[i], high[i], low[i])
                valid_zone := false                
                break            
    else         
        valid_zone := false                
    valid_zone
        
display_zones(boxes, labels, dzones, show_last, zsize) =>
    for i = 0 to math.min(show_last - 1, zsize - 1)
        zindex = zsize - (1 + i)
        get_box = array.get(boxes, i)
        get_label = array.get(labels, i)
        zone = array.get(dzones, zindex)

        box.set_lefttop(get_box, zone.left, zone.top)
        box.set_rightbottom(get_box, zone.left, zone.bottom)
        box.set_border_color(get_box, zone.css)
        box.set_bgcolor(get_box, zone.css)
        label.set_textcolor(get_label, color.new(zone.css, 0))
        label.set_xy(get_label, bar_index + 5, zone.bottom)

        if zone.type == TBD_ZONE        
            label.set_text(get_label, "")
        else
            label.set_text(get_label, "Volume change: " + str.tostring(zone.volume) + "%\nReached: " + str.tostring(zone.reached))            
        
//------------------------------------------------------------------
// CALCULATE ZONES
//------------------------------------------------------------------
if barstate.isfirst
    for i = 0 to showlast - 1
        array.push(zone_boxes, box.new(na,na,na,na, xloc = xloc.bar_time , extend = extend.right))   
        array.push(zone_labels, label.new(na,na,na, size = label_size, style = label.style_none))

if barstate.isconfirmed    
    if is_indecision_bar()
        period_volume = get_volume(include_current_bar = false, bars = volume_periods)
        array.push(zones, Zone.new(top = high, bottom = low, volume = period_volume, left = time, css= indecision_zone_css))

    for zone in zones     
        index = array.indexof(zones, zone)
        if zone.type == TBD_ZONE and zone.left == time[confirm_bars]
            // Confirm zone o delete it           
            if confirm_zone(zone)
                demand_zone_confirmed := zone.type == DEMAND_ZONE
                supply_zone_confirmed := zone.type == SUPPLY_ZONE
                for i = 1 to confirm_bars
                    pindex = index + 1
                    if pindex < array.size(zones)
                        array.remove(zones, pindex) 
            else 
                array.remove(zones, index)
            break

    for zone in zones      
        index = array.indexof(zones, zone)
        if not valid_zone(zone, close, high, low) 
            if last_zone_reached.left == zone.left
                last_zone_reached := Zone.new()
            array.remove(zones, index)
        else    
            // Check if price reached any zone
            if zone.type == DEMAND_ZONE and bar_reached_zone(zone, low)            
                zone.reached += 1    
                price_enter_demand_zone := true

            if zone.type == SUPPLY_ZONE and bar_reached_zone(zone, high)            
                zone.reached += 1                
                price_enter_supply_zone := true

            if price_enter_demand_zone or price_enter_supply_zone
                zone.bars_outside := 0
                last_zone_reached := zone

    if last_zone_reached.type == DEMAND_ZONE and close > last_zone_reached.top //and open > last_zone_reached.top            
        last_zone_reached.bars_outside += 1
    else if last_zone_reached.type == SUPPLY_ZONE and close < last_zone_reached.bottom //and open < last_zone_reached.bottom
        last_zone_reached.bars_outside += 1

if barstate.islast
    int zones_size = array.size(zones)
    if zones_size > 0
        display_zones(zone_boxes, zone_labels, zones, showlast, zones_size)

//------------

// Suplus

// Inputs
ExtendLines1 =true
ext_active =true
ShowLabel = true
label_loc = 'Right'
label_offset = 15
show_HL = true
show_close =true
LineStyleHLInput ='Solid'
LineWidthHLInput = 1
LineStyleCloseInput = 'Solid'
LineWidthCloseInput = 1

var string LineStyleHL = na

LineStyleHL := if LineStyleHLInput == 'Solid'
    line.style_solid
else if LineStyleHLInput == 'Dotted'
    line.style_dotted
else if LineStyleHLInput == 'Dashed'
    line.style_dashed

var string LineStyleClose = na

LineStyleClose := if LineStyleCloseInput == 'Solid'
    line.style_solid
else if LineStyleCloseInput == 'Dotted'
    line.style_dotted
else if LineStyleCloseInput == 'Dashed'
    line.style_dashed

// Time Frame 1 = TF1
TF1_Menu = input.string(title='Surplus And Deficit Zones', defval='Surplus/Deficit', options=['Surplus/Deficit', 'Surplus/Deficit Zone', 'Disable'], group='Surplus and Deficit Settings')
TF1_input = input.string(title='Time Frame', defval='Chart', options=['Chart', '3m', '5m', '15m', '30m', '45m', '1h', '2h', '3h', '4h', '6h', '8h', '12h', 'D', '3D', 'W', '2W', '1M'], group='Surplus and Deficit Settings')
TF1_VolMA1Input = 6
TF1_NumZones =input(5,title = "Previous Zones:",group='Surplus and Deficit Settings') 
TF1_extRight = false
TF1_ResLinesColor = color(color.new(color.red, 20))
TF1_ResZoneColor = color(color.new(color.red, 90))
TF1_SupLinesColor = color(color.new(color.lime, 20))
TF1_SupZoneColor = color(color.new(color.lime, 90))


f_TFx(_TF_input) =>
    if _TF_input == 'Chart'
        timeframe.period
    else if _TF_input == '3m'
        '3'
    else if _TF_input == '5m'
        '5'
    else if _TF_input == '15m'
        '15'
    else if _TF_input == '30m'
        '30'
    else if _TF_input == '45m'
        '45'
    else if _TF_input == '1h'
        '60'
    else if _TF_input == '2h'
        '120'
    else if _TF_input == '3h'
        '180'
    else if _TF_input == '4h'
        '240'
    else if _TF_input == '6h'
        '360'
    else if _TF_input == '8h'
        '480'
    else if _TF_input == '12h'
        '720'
    else if _TF_input == 'D'
        'D'
    else if _TF_input == '3D'
        '3D'
    else if _TF_input == 'W'
        'W'
    else if _TF_input == '2W'
        '2W'
    else if _TF_input == '1M'
        '1M'

TF1 = f_TFx(TF1_input)

vol_check = na(volume)
var table vol_check_table = na
if barstate.islast and vol_check
    table.delete(vol_check_table)
    vol_check_table := table.new(position=position.middle_right, columns=1, rows=1, frame_color=color.red, frame_width=1)
    table.cell(vol_check_table, column=0, row=0, text='There is no volume data for this symbol' + ' (' + syminfo.tickerid + ')' + '\n Please use a different symbol with volume data', text_color=color.red)

// // --------- This ensures that no plots from lower time frames will be plotted on higher time frames.
// ————— Converts current chart resolution into a float minutes value.
f_resInMinutes() =>
    _resInMinutes = timeframe.multiplier * (timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na)
    _resInMinutes
// ————— Returns the float minutes value of the string _res.
f_tfResInMinutes(_res) =>
    // _res: resolution of any TF (in "timeframe.period" string format).
    // Dependency: f_resInMinutes().
    request.security(syminfo.tickerid, _res, f_resInMinutes())

// —————————— Determine if current timeframe is smaller that higher timeframe selected in Inputs.
// Get higher timeframe in minutes.
TF1InMinutes = f_tfResInMinutes(TF1)


// Get current timeframe in minutes.
currentTFInMinutes = f_resInMinutes()
// Compare current TF to higher TF to make sure it is smaller, otherwise our plots don't make sense.
chartOnLowerTF1 = currentTFInMinutes <= TF1InMinutes


TF1_inH = str.tostring(TF1InMinutes / 60)
TF1_text = TF1InMinutes >= 60 and TF1InMinutes < 1440 ? TF1_inH + 'h' : TF1InMinutes < 60 ? TF1 + 'm' : TF1


bool TF1_newbar = ta.change(time(TF1)) != 0
TF1_bi1 = ta.valuewhen(TF1_newbar, bar_index, 1)
TF1_bi5 = ta.valuewhen(TF1_newbar, bar_index, 5)
TF1_bb1 = bar_index-TF1_bi1
TF1_bb5 = bar_index-TF1_bi5
TF1_br = TF1_bb5 - TF1_bb1



var int TF1_Hi_Bi = na
var int TF1_Lo_Bi = na
var int TF2_Hi_Bi = na
var int TF2_Lo_Bi = na


if TF1_bb1 > 4999 or (TF1_bb1 + TF1_br) > 4999
    TF1_Hi_Bi := 4999
    TF1_Lo_Bi := 4999
else
    TF1_Hi_Bi := math.abs(ta.highestbars(high, nz(TF1_br, 1)))[TF1_bb1] + TF1_bb1
    TF1_Lo_Bi := math.abs(ta.lowestbars(low, nz(TF1_br, 1)))[TF1_bb1] + TF1_bb1





// TFUp and TFDown Calculations
f_tfUp(_TF_High, _TF_Vol, _TF_VolMA) =>
    _TF_High[3] > _TF_High[4] and _TF_High[4] > _TF_High[5] and _TF_High[2] < _TF_High[3] and _TF_High[1] < _TF_High[2] and _TF_Vol[3] > _TF_VolMA[3]
f_tfDown(_TF_Low, _TF_Vol, _TF_VolMA) =>
    _TF_Low[3] < _TF_Low[4] and _TF_Low[4] < _TF_Low[5] and _TF_Low[2] > _TF_Low[3] and _TF_Low[1] > _TF_Low[2] and _TF_Vol[3] > _TF_VolMA[3]

// Function for each time frame's various sources used in FractalUp and FractalDown calculations.
f_tfSources(_res, _source) =>
    request.security(syminfo.tickerid, _res, _source)

// Line and label arrays
var TF1_UpperSupportLine_array = array.new_line(TF1_NumZones)
var TF1_LowerSupportLine_array = array.new_line(TF1_NumZones)
var TF1SupLabel_array = array.new_label(1)

var TF1_UpperResLine_array = array.new_line(TF1_NumZones)
var TF1_LowerResLine_array = array.new_line(TF1_NumZones)
var TF1ResLabel_array = array.new_label(1)

// Resistance Line Functions
TF_ResistanceLineA(TF_input,TF_FractalUp,TF_ResLineColor,TF_UpperResLine_array,TF_NumZones,TF_ResZone, TF_LowerResLine_array,TF_text,TF_ResLabel_array,bi_hi,bi_3,bi,bi_2,ext_right) =>
    if show_HL
        UpperResistanceLine = line.new(x1=TF_input != 'Chart' ? bi_hi : bi_3, y1=TF_FractalUp, x2=bi, y2=TF_FractalUp, color=TF_ResLineColor, style=LineStyleHL, width=LineWidthHLInput, extend=extend.right)
        line.set_extend(id=array.get(TF_UpperResLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_UpperResLine_array, TF_NumZones-1), x=TF_input != 'Chart' ? bi_hi : bi_3)
        array.push(TF_UpperResLine_array, UpperResistanceLine)
        line.delete(array.shift(TF_UpperResLine_array))
    if show_close
        LowerResistanceLine = line.new(x1=TF_input != 'Chart' ? bi_hi : bi_3, y1=TF_ResZone, x2=bi, y2=TF_ResZone, color=TF_ResLineColor, style=LineStyleClose, width=LineWidthCloseInput, extend=extend.right)
        line.set_extend(id=array.get(TF_LowerResLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_LowerResLine_array, TF_NumZones-1), x=TF_input != 'Chart' ? bi_hi : bi_3)
        array.push(TF_LowerResLine_array, LowerResistanceLine)
        line.delete(array.shift(TF_LowerResLine_array))
    if ShowLabel == true and label_loc == 'Left'
        TFResLabel = label.new(TF_input != 'Chart' ? bi_hi : bi_2, TF_FractalUp, text=TF_text + "(Surplus)", color=color.new(color.white, 100), size=size.normal, style=label.style_label_right, textcolor=TF_ResLineColor)
        array.push(TF_ResLabel_array, TFResLabel)
        label.delete(array.shift(TF_ResLabel_array))

TF_ResistanceLineB(TF_FractalUp,TF_ResLineColor,TF_UpperResLine_array,TF_NumZones,TF_ResZone,TF_LowerResLine_array,TF_text,TF_ResLabel_array,bi3,bi,ext_right) =>
    if show_HL
        UpperResistanceLine = line.new(x1=bi3, y1=TF_FractalUp, x2=bi, y2=TF_FractalUp, color=TF_ResLineColor, style=LineStyleHL, width=LineWidthHLInput, extend=extend.right)
        line.set_extend(id=array.get(TF_UpperResLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_UpperResLine_array, TF_NumZones-1), x=bi3)
        array.push(TF_UpperResLine_array, UpperResistanceLine)
        line.delete(array.shift(TF_UpperResLine_array))
    if show_close
        LowerResistanceLine = line.new(x1=bi3, y1=TF_ResZone, x2=bi, y2=TF_ResZone, color=TF_ResLineColor, style=LineStyleClose, width=LineWidthCloseInput, extend=extend.right)
        line.set_extend(id=array.get(TF_LowerResLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_LowerResLine_array, TF_NumZones-1), x=bi3)
        array.push(TF_LowerResLine_array, LowerResistanceLine)
        line.delete(array.shift(TF_LowerResLine_array))
    if ShowLabel == true and label_loc == 'Left'
        TFResLabel = label.new(bi3, TF_FractalUp, text=TF_text + "(Surplus)", color=color.new(color.white, 100), size=size.normal, style=label.style_label_right, textcolor=TF_ResLineColor)
        array.push(TF_ResLabel_array, TFResLabel)
        label.delete(array.shift(TF_ResLabel_array))

// Support Line Functions
TF_SupportLineA(TF_input, TF_FractalDown,TF_SupLinesColor,TF_UpperSupportLine_array,TF_NumZones,TF_SupportZone, TF_LowerSupportLine_array,TF_text,TF_SupLabel_array,bi_lo,bi_3,bi,bi_2,ext_right) =>
    if show_close
        UpperSupportLine = line.new(x1=TF_input != 'Chart' ? bi_lo : bi_3, y1=TF_SupportZone, x2=bi, y2=TF_SupportZone, color=TF_SupLinesColor, style=LineStyleClose, width=LineWidthCloseInput, extend=extend.right)
        line.set_extend(id=array.get(TF_UpperSupportLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_UpperSupportLine_array, TF_NumZones-1), x=TF_input != 'Chart' ? bi_lo : bi_3)
        array.push(TF_UpperSupportLine_array, UpperSupportLine)
        line.delete(array.shift(TF_UpperSupportLine_array))
    if show_HL
        LowerSupportLine = line.new(x1=TF_input != 'Chart' ? bi_lo : bi_3, y1=TF_FractalDown, x2=bi, y2=TF_FractalDown, color=TF_SupLinesColor, style=LineStyleHL, width=LineWidthHLInput, extend=extend.right)
        line.set_extend(id=array.get(TF_LowerSupportLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_LowerSupportLine_array, TF_NumZones-1), x=TF_input != 'Chart' ? bi_lo : bi_3)
        array.push(TF_LowerSupportLine_array, LowerSupportLine)
        line.delete(array.shift(TF_LowerSupportLine_array))
    if ShowLabel == true and label_loc == 'Left'
        SupLabel = label.new(TF_input != 'Chart' ? bi_lo : bi_2, TF_FractalDown, text=TF_text + "(Deficit)", color=color.new(color.white, 100), size=size.normal, style=label.style_label_right, textcolor=TF_SupLinesColor)
        array.push(TF_SupLabel_array, SupLabel)
        label.delete(array.shift(TF_SupLabel_array))

TF_SupportLineB(TF_FractalDown,TF_SupLinesColor,TF_UpperSupportLine_array,TF_NumZones,TF_SupportZone,TF_LowerSupportLine_array,TF_text,TF_SupLabel_array,bi3,bi,ext_right) =>
    if show_close
        UpperSupportLine = line.new(x1=bi3, y1=TF_SupportZone, x2=bi, y2=TF_SupportZone, color=TF_SupLinesColor, style=LineStyleClose, width=LineWidthCloseInput, extend=extend.right)
        line.set_extend(id=array.get(TF_UpperSupportLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_UpperSupportLine_array, TF_NumZones-1), x=bi3)
        array.push(TF_UpperSupportLine_array, UpperSupportLine)
        line.delete(array.shift(TF_UpperSupportLine_array))
    if show_HL
        LowerSupportLine = line.new(x1=bi3, y1=TF_FractalDown, x2=bi, y2=TF_FractalDown, color=TF_SupLinesColor, style=LineStyleHL, width=LineWidthHLInput, extend=extend.right)
        line.set_extend(id=array.get(TF_LowerSupportLine_array, TF_NumZones-1), extend=ext_right ? extend.right : extend.none)
        if ExtendLines1 == true
            line.set_x2(id=array.get(TF_LowerSupportLine_array, TF_NumZones-1), x=bi3)
        array.push(TF_LowerSupportLine_array, LowerSupportLine)
        line.delete(array.shift(TF_LowerSupportLine_array))
    if ShowLabel == true and label_loc == 'Left'
        SupLabel = label.new(bi3, TF_FractalDown, text=TF_text + "(Deficit)", color=color.new(color.white, 100), size=size.normal, style=label.style_label_right, textcolor=TF_SupLinesColor)
        array.push(TF_SupLabel_array, SupLabel)
        label.delete(array.shift(TF_SupLabel_array))

// Label Function
TFLabel(bi, TF_Fractal, txt, txtcolor, TFLabel_array) =>
    Label = label.new(bi, TF_Fractal, text=txt, size=size.normal, style=label.style_none, textcolor=txtcolor)
    array.push(TFLabel_array, Label)
    label.delete(array.shift(TFLabel_array))

// Surplus/Deficit  = Time Frame 1 = TF1
TF1_Vol = f_tfSources(TF1, volume)
TF1_VolMA = ta.sma(TF1_Vol, TF1_VolMA1Input)
TF1_High = f_tfSources(TF1, high)
TF1_Low = f_tfSources(TF1, low)
TF1_Open = f_tfSources(TF1, open)
TF1_Close = f_tfSources(TF1, close)

TF1_Up = f_tfUp(TF1_High, TF1_Vol, TF1_VolMA)
TF1_Down = f_tfDown(TF1_Low, TF1_Vol, TF1_VolMA)

TF1_CalcFractalUp() =>
    TF1_FractalUp = 0.0
    TF1_FractalUp := TF1_Up ? TF1_High[3] : TF1_FractalUp[1]
    TF1_FractalUp

TF1_CalcFractalDown() =>
    TF1_FractalDown = 0.0
    TF1_FractalDown := TF1_Down ? TF1_Low[3] : TF1_FractalDown[1]
    TF1_FractalDown

TF1_FractalUp = request.security(syminfo.tickerid, TF1, TF1_CalcFractalUp())
TF1_FractalDown = request.security(syminfo.tickerid, TF1, TF1_CalcFractalDown())

// Zones - Current Time Frame = Time Frame 1 = TF1
// Fractal Up Zones
TF1_CalcFractalUpZone() =>
    TF1_FractalUpZone = 0.0
    TF1_FractalUpZone := TF1_Up and TF1_Close[3] >= TF1_Open[3] ? TF1_Close[3] : TF1_Up and TF1_Close[3] < TF1_Open[3] ? TF1_Open[3] : TF1_FractalUpZone[1]
    TF1_FractalUpZone

TF1_FractalUpZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalUpZone())
TF1_ResZone = TF1_FractalUpZone

// Fractal Down Zones
TF1_CalcFractalDownZone() =>
    TF1_FractalDownZone = 0.0
    TF1_FractalDownZone := TF1_Down and TF1_Close[3] >= TF1_Open[3] ? TF1_Open[3] : TF1_Down and TF1_Close[3] < TF1_Open[3] ? TF1_Close[3] : TF1_FractalDownZone[1]
    TF1_FractalDownZone

TF1_FractalDownZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalDownZone())
TF1_SupportZone = TF1_FractalDownZone

// Time Frame 1 = TF1 Resistance
if (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit') and TF1_FractalUp != TF1_FractalUp[1] and chartOnLowerTF1 
    TF_ResistanceLineA(TF1_input,TF1_FractalUp,TF1_ResLinesColor,TF1_UpperResLine_array,TF1_NumZones,TF1_ResZone, TF1_LowerResLine_array,TF1_text,TF1ResLabel_array,bar_index[TF1_Hi_Bi], bar_index[3], bar_index,bar_index[2], TF1_extRight)
else if (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit') and na(TF1_FractalUp != TF1_FractalUp[1]) and chartOnLowerTF1 and na(ta.barssince(TF1_FractalUp != TF1_FractalUp[1])) 
    TF_ResistanceLineB(TF1_FractalUp,TF1_ResLinesColor,TF1_UpperResLine_array,TF1_NumZones,TF1_ResZone,TF1_LowerResLine_array,TF1_text,TF1ResLabel_array,bar_index[3],bar_index, TF1_extRight)

if (TF1_Menu == 'Surplus/Deficit Zone')
    linefill.new(array.get(TF1_UpperResLine_array, TF1_NumZones-1), array.get(TF1_LowerResLine_array, TF1_NumZones-1), TF1_ResZoneColor)

if ShowLabel == true and (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit')
    TFLabel(bar_index+label_offset, TF1_FractalUp, TF1_text+"(Surplus)", TF1_ResLinesColor, TF1ResLabel_array)


// Time Frame 1 = TF1 Support
if (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit') and TF1_FractalDown != TF1_FractalDown[1]
    TF_SupportLineA(TF1_input,TF1_FractalDown,TF1_SupLinesColor,TF1_UpperSupportLine_array,TF1_NumZones,TF1_SupportZone, TF1_LowerSupportLine_array,TF1_text,TF1SupLabel_array,bar_index[TF1_Lo_Bi], bar_index[3], bar_index,bar_index[2], TF1_extRight)
else if (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit') and na(TF1_FractalDown != TF1_FractalDown[1]) and chartOnLowerTF1 and na(ta.barssince(TF1_FractalDown != TF1_FractalDown[1])) 
    TF_SupportLineB(TF1_FractalDown,TF1_SupLinesColor,TF1_UpperSupportLine_array,TF1_NumZones,TF1_SupportZone,TF1_LowerSupportLine_array,TF1_text,TF1SupLabel_array,bar_index[3],bar_index, TF1_extRight)

if (TF1_Menu == 'Surplus/Deficit Zone')
    linefill.new(array.get(TF1_UpperSupportLine_array, TF1_NumZones-1), array.get(TF1_LowerSupportLine_array, TF1_NumZones-1), TF1_SupZoneColor)

if ShowLabel == true and (TF1_Menu == 'Surplus/Deficit Zone' or TF1_Menu == 'Surplus/Deficit') and chartOnLowerTF1 
    TFLabel(bar_index+label_offset, TF1_FractalDown, TF1_text+"(Deficit)", TF1_SupLinesColor, TF1SupLabel_array)

if ext_active == false and barstate.islast
    line.set_extend(array.get(TF1_UpperResLine_array, TF1_NumZones-1), extend.none)
    line.set_x2(array.get(TF1_UpperResLine_array, TF1_NumZones-1), bar_index)
    line.set_extend(array.get(TF1_LowerResLine_array, TF1_NumZones-1), extend.none)
    line.set_x2(array.get(TF1_LowerResLine_array, TF1_NumZones-1), bar_index)

if ext_active == false and barstate.islast
    line.set_extend(array.get(TF1_UpperSupportLine_array, TF1_NumZones-1), extend.none)
    line.set_x2(array.get(TF1_UpperSupportLine_array, TF1_NumZones-1), bar_index)
    line.set_extend(array.get(TF1_LowerSupportLine_array, TF1_NumZones-1), extend.none)
    line.set_x2(array.get(TF1_LowerSupportLine_array, TF1_NumZones-1), bar_index)

